package ch.ethz.fcl.metrobuzz.tools;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;

import javax.vecmath.Vector2d;

import org.lwjgl.opengl.GL11;

import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Point2D;

public class MBGL {
	public static void render(List<Point2D> points, int mode) {
		GL11.glBegin(mode);
		for (int i = 0; i < points.size(); i++)
			render(points.get(i));
		GL11.glEnd();
	}

	public static void render(Point2D[] points, int mode) {
		GL11.glBegin(mode);
		for (int i = 0; i < points.length; i++)
			render(points[i]);
		GL11.glEnd();
	}

	public static void render(Point2D point) {
		GL11.glVertex3f(point.getX(), point.getY(), 0);
	}

	public static void render(Point2D p1, float width1, Point2D p2, float width2,
			boolean sideLine, boolean frontBackLine) {
		Vector2d dir = new Vector2d(p2.getX() - p1.getX(), p2.getY()
				- p1.getY());
		Vector2d verDir1 = MBTools.rotate(dir, Math.PI / 2);
		Vector2d verDir2 = MBTools.rotate(dir, -Math.PI / 2);

		Point2D p11 = p1.getPointAt(verDir1, width1 / 2);
		Point2D p12 = p1.getPointAt(verDir2, width1 / 2);

		Point2D p21 = p2.getPointAt(verDir1, width2 / 2);
		Point2D p22 = p2.getPointAt(verDir2, width2 / 2);
		GL11.glBegin(GL11.GL_QUADS);
		render(p11);
		render(p12);
		render(p22);
		render(p21);
		GL11.glEnd();

		GL11.glColor4f(0.1f, 0.1f, 0.1f, 0.8f);
		GL11.glLineWidth(1.0f);
		if (sideLine) {
			GL11.glBegin(GL11.GL_LINES);
			render(p11);
			render(p21);

			render(p12);
			render(p22);
			GL11.glEnd();
		} if(frontBackLine){
			GL11.glBegin(GL11.GL_LINES);
			render(p11);
			render(p12);

			render(p21);
			render(p22);
			GL11.glEnd();
		}
	}

	public static void renderWithMid(Point2D p1, Point2D p2, float width) {
		Vector2d dir = p1.getDir(p2);

		float length = p1.distance(p2);
		Point2D pMid = p1.getPointAt(p2, 0.5f);
		Vector2d verDir1 = MBTools.rotate(dir, Math.PI / 2);
		Vector2d verDir2 = MBTools.rotate(dir, -Math.PI / 2);

		Point2D pMidUp = pMid.getPointAt(verDir1, length / 2);

		Point2D p11 = p1.getPointAt(verDir1, width / 2);
		Point2D p12 = p1.getPointAt(verDir2, width / 2);

		Point2D pMidUp1 = pMidUp.getPointAt(verDir1, width / 2);
		Point2D pMidUp2 = pMidUp.getPointAt(verDir2, width / 2);

		Point2D p21 = p2.getPointAt(verDir1, width / 2);
		Point2D p22 = p2.getPointAt(verDir2, width / 2);

		GL11.glBegin(GL11.GL_QUADS);
		render(p11);
		render(p12);
		render(pMidUp2);
		render(pMidUp1);
		GL11.glEnd();

		GL11.glLineWidth(1.0f);
		GL11.glBegin(GL11.GL_LINES);
		render(p11);
		render(pMidUp1);

		render(p12);
		render(pMidUp2);
		GL11.glEnd();

		GL11.glBegin(GL11.GL_QUADS);
		render(p21);
		render(p22);
		render(pMidUp2);
		render(pMidUp1);
		GL11.glEnd();

		GL11.glLineWidth(1.0f);
		GL11.glBegin(GL11.GL_LINES);
		render(p21);
		render(pMidUp1);

		render(p22);
		render(pMidUp2);
		GL11.glEnd();
	}

	public static void renderEclipse(float cx, float cy, float cz, float a,
			float b, boolean solid) {
		if (solid) {
			GL11.glBegin(GL11.GL_TRIANGLE_FAN);
			GL11.glVertex3f(cx, cy, cz);
		} else
			GL11.glBegin(GL11.GL_LINE_STRIP);

		int slices = 100;
		for (int i = 0; i <= slices; i++) {
			float x = (float) (cx + a * Math.cos(Math.PI * 2 / slices * i));
			float y = (float) (cy + b * Math.sin(Math.PI * 2 / slices * i));
			GL11.glVertex3f(x, y, cz);
		}

		GL11.glEnd();
	}

	public static void restoreViewMatrix() {
		// restore previous projection view
		GL11.glPopMatrix();
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glPopMatrix();
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
	}

	public static void enableBlend() {
		GL11.glEnable(GL11.GL_BLEND);
		GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

		GL11.glEnable(GL11.GL_POINT_SMOOTH);
		GL11.glHint(GL11.GL_POINT_SMOOTH_HINT, GL11.GL_NICEST);

		GL11.glEnable(GL11.GL_LINE_SMOOTH);
		GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST);

		GL11.glEnable(GL11.GL_POLYGON_SMOOTH);
		GL11.glHint(GL11.GL_POLYGON_SMOOTH_HINT, GL11.GL_NICEST);
	}

	public static void disableBlend() {
		GL11.glDisable(GL11.GL_BLEND);
	}

	public static void color(float[] color) {
		if (color.length == 3)
			GL11.glColor3f(color[0], color[1], color[2]);

		if (color.length == 4)
			GL11.glColor4f(color[0], color[1], color[2], color[3]);
	}

	public static void addNativeLibrary() {
		addDir("/lwjgl");
	}

	private static void addDir(String s) {
		try {
			try {
				// This enables the java.library.path to be modified at runtime
				// From a Sun engineer at
				// http://forums.sun.com/thread.jspa?threadID=707176
				//
				Field field = ClassLoader.class.getDeclaredField("usr_paths");
				field.setAccessible(true);
				String[] paths = (String[]) field.get(null);
				for (int i = 0; i < paths.length; i++) {
					if (s.equals(paths[i])) {
						return;
					}
				}
				String[] tmp = new String[paths.length + 1];
				System.arraycopy(paths, 0, tmp, 0, paths.length);
				tmp[paths.length] = s;
				field.set(null, tmp);
				System.setProperty("java.library.path",
						System.getProperty("java.library.path")
								+ File.pathSeparator + s);
			} catch (IllegalAccessException e) {
				throw new IOException(
						"Failed to get permissions to set library path");
			} catch (NoSuchFieldException e) {
				throw new IOException(
						"Failed to get field handle to set library path");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
